#ifndef MIDI_OUT_DEVICE_H
#define MIDI_OUT_DEVICE_H


#pragma warning(disable:4786) // Disable annoying template warnings


// Necessary for Windows data types
#include "stdafx.h"
#include <mmsystem.h>

// Necessary for exception classes derived from std::exception
#include <exception> 

// Necessary for header queue used by CHeaderQueue
#include <queue>


namespace midi
{
    // Encapsulates the midiOutGetErrorText messages
    class CMIDIOutException : public std::exception
    {
    public:
        CMIDIOutException(MMRESULT ErrCode) throw()
        { ::midiOutGetErrorText(ErrCode, m_ErrMsg, sizeof m_ErrMsg); }

        const char *what() const throw() { return m_ErrMsg; }

    private:
        char m_ErrMsg[128];
    };


    // Thrown when memory allocation fails within a CMIDIOutDevice 
    // object
    class CMIDIOutMemFailure : public std::bad_alloc
    {
    public:
        const char *what() const throw()
        { return "Memory allocation within a CMIDIOutDevice object "
                 "failed."; }
    };


    // Thrown when a CMIDIOutDevice is unable to create a signalling 
    // event
    class CMIDIOutEventFailure : public std::exception
    {
    public:
        const char *what() const throw()
        { return "Unable to create a signalling event for "
                 "CMIDIOutDevice object."; }
    };


    // Thrown when a CMIDIOutDevice is unable to create a worker 
    // thread
    class CMIDIOutThreadFailure : public std::exception
    {
    public:
        const char *what() const throw()
        { return "Unable to create worker thread for CMIDIOutDevice "
                 "object."; }
    };


    //----------------------------------------------------------------
    // CMIDIOutDevice
    //
    // This class represents MIDI output devices.
    //----------------------------------------------------------------


    class CMIDIOutDevice
    {
    public:
        // For constructing a CMIDIOutDevice in an closed state
        CMIDIOutDevice();

        // Destruction
        ~CMIDIOutDevice();

        // Opens the MIDI output device
        void Open(UINT DeviceId);

        // Closes the MIDI output device
        void Close();

        // Sends short message
        void SendMsg(DWORD Msg);

        // Gets the number of MIDI output devices on this system
        static UINT GetNumDevs() { return midiOutGetNumDevs(); }

        // Gets the capabilities of a particular MIDI output device
        // The results are stored in the MIDIOUTCAPS parameter.
        static void GetDevCaps(UINT DeviceId, MIDIOUTCAPS &Caps);


    // Private methods
    private:
        // Copying and assignment not allowed
        CMIDIOutDevice &operator = (const CMIDIOutDevice &);

        // Called by Windows when a MIDI output event occurs
        static void CALLBACK MidiOutProc(HMIDIOUT MidiOut, UINT Msg,
                                         DWORD_PTR Instance, DWORD_PTR Param1, 
                                         DWORD_PTR Param2);
    private:
        HMIDIOUT       m_DevHandle;
        HANDLE         m_Event;
        enum State { CLOSED, OPENED } m_State;
    };
}


#endif
